﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using shaders;

namespace DARE
{
    public class CSprite3D : ARenderable //, IOrderedDraw
    {
        
    #region fields

        protected Texture2D m_texture;
        protected VertexBuffer m_vertexBuffer;
        protected IndexBuffer m_indexBuffer;
        protected CEffect m_basicEffect;
        private string m_technique;
        protected Color m_color = Color.White;
        protected Rectangle m_rect;
        protected Vector2 m_size = Vector2.Zero;
        protected bool m_isSizeFixed = true;
        protected Vector3 m_offset = Vector3.Zero;

        // used to order the sprites
        internal float cameraDistance = -1;

        //private bool m_transparency = false;
        //private uint m_effectId = 0;
        //private bool m_isVisible = true;

        CRasterizationContent m_rasterization;

    #endregion

    #region properties

        //public bool IsInTheOrderer { get; set; }

        public virtual Texture2D Texture
        {
            get { return m_texture; }
            set 
            { 
                m_texture = value;
                if (m_size == Vector2.Zero)
                    m_size = new Vector2(m_texture.Width, m_texture.Height); Actualize(); 
            }
        }

        public virtual Vector2 SpriteOffset { get; set; }

        public bool IsSizeFixed
        { 
            get { return m_isSizeFixed; } 
            set { m_isSizeFixed = value; } 
        }

        public virtual Color Color 
        { 
            get { return m_color; } 
            set { m_color = value; Actualize(); } 
        }

        public virtual Vector2 Size 
        { 
            get { return m_size; } 
            set { m_size = value; Actualize(); } 
        }

        public Vector3 Offset
        {
            get { return m_offset; }
            set { m_offset = value; }
        }

        public Vector3 Position
        {
            get { return m_node.Position + m_offset; }
        }


        ////for the orderer
        //public uint EffectId
        //{
        //    get { return m_effectId; }
        //    set
        //    {
        //        if (this.m_effectId != value)
        //        {
        //            this.m_effectId = value;
        //            this.NodeInformationChanged = true;
        //        }
        //    }
        //}
        
        //public string UniqueId
        //{
        //    get { throw new NotImplementedException(); }
        //}

        //public float CameraDistance
        //{
        //    get
        //    {
        //        if (m_node == null || 
        //            m_node.Render3D.CameraMgr.ActiveCamera == null)
        //            return 0.0f;
        //        return Vector3.Distance(m_node.Position,
        //                                m_node.Render3D.CameraMgr.ActiveCamera.Position); ;
        //    }
        //}

        //public bool IsTransparency
        //{
        //    get { return this.m_transparency; }
        //    set
        //    {
        //         if (this.m_transparency != value)
        //        {
        //            this.m_transparency = value;
        //            this.TransparencyChanged.Invoke(this, !value);
        //        }
        //    }
        //}

        //public CRasterizationContent State { get { return m_rasterization; } }

        //public bool NodeInformationChanged { get; set; }

        //public bool Visible
        //{
        //    get { return this.m_isVisible; }
        //    set
        //    {
        //        if (this.m_isVisible != value)
        //            this.m_isVisible = value;
        //    }
        //}

        //public bool CanSort
        //{
        //    get { return false; }
        //}

    #endregion

    #region ctor

        private void ctor()
        {
            m_basicEffect = new CEffect();
            
            //m_technique = m_basicEffect.CurrentTechnique.Name;
            m_vertexBuffer = new VertexBuffer(CDare.Instance.GraphicsDevice, typeof(VertexPositionNormalTexture), 4, BufferUsage.None);
            VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[4]
            {
                new VertexPositionNormalTexture(new Vector3(-.5f, -.5f, 0), new Vector3(0, 0, -1), new Vector2(1, 1)),
                new VertexPositionNormalTexture(new Vector3(.5f, -.5f, 0), new Vector3(0, 0, -1), new Vector2(0, 1)),
                new VertexPositionNormalTexture(new Vector3(.5f, .5f, 0), new Vector3(0, 0, -1), new Vector2(0, 0)),
                new VertexPositionNormalTexture(new Vector3(-.5f, .5f, 0), new Vector3(0, 0, -1), new Vector2(1, 0))
            };
            m_vertexBuffer.SetData(vertices);
            m_indexBuffer = new IndexBuffer(CDare.Instance.GraphicsDevice, IndexElementSize.SixteenBits, 6, BufferUsage.None);
            short[] indices = new short[6]
            {
                0, 1, 2, 0, 2, 3
            };
            m_indexBuffer.SetData(indices);
            //m_transparency = true;
            m_rasterization = new CRasterizationContent(RasterizerState.CullCounterClockwise);
        }

        public CSprite3D()
        {
            ctor();
        }

        public CSprite3D(string name)
        {
            m_name = name;
            ctor();
        }

        public virtual CSprite3D Clone()
        {
            return this;
        }
        
    #endregion

    #region events

        //public event DataChanged<IOrderedDraw, bool> TransparencyChanged;

    #endregion

    #region run

        //public int Compare(IOrderedDraw a, IOrderedDraw b)
        //{
        //    throw new NotImplementedException();
        //}

        protected virtual void Actualize()
        {

        }
 
        public override void Update(GameTime gameTime)
        {
            if (CDare.Instance.HasWindowChanged)
                Actualize();
            //CRender3D r = m_node.Render3D;

            //CNode nd = m_node.Parent;

            //r.RemoveNode(m_node);
            //r.AttachNode(m_node);

            //if (nd != null)
                //m_node.SetParent(nd);
        }

        //public void InitializeDraw(GameTime gameTime)
        //{

        //}

        public override void Draw(GameTime gameTime)
        {
            //BoundingFrustum bf = new BoundingFrustum(m_node.Render3D.CameraMgr.ActiveCamera.View * m_node.Render3D.CameraMgr.ActiveCamera.Projection);

            if (m_node.Render3D == null)
                return;

            // ==================== BUG if not set it will flip the sprite when outside of the screen ===============
            BoundingFrustum bf = m_node.Render3D.CameraMgr.ActiveCamera.Frustrum;
            if (bf.Contains(m_node.Position) == ContainmentType.Disjoint)
                return;
            // ==============================================================================

            //if (m_entity.Model != null && m_entity.Model.Effect.Parameters["isRenderingShadows"].GetValueBoolean())
           //     return;
            //m_basicEffect.CurrentTechnique = m_basicEffect.Techniques[m_technique];

            if (!m_isSizeFixed)
            {
                m_basicEffect.UseSceneParameters = false;
                m_basicEffect.World = Matrix.CreateBillboard(m_node.Position + m_offset, m_node.Render3D.CameraMgr.ActiveCamera.Position, m_node.Render3D.CameraMgr.ActiveCamera.Up, null) * 
                    Matrix.CreateFromQuaternion(m_node.Orientation);
                m_basicEffect.View = m_node.Render3D.CameraMgr.ActiveCamera.View;
                m_basicEffect.Projection = m_node.Render3D.CameraMgr.ActiveCamera.Projection;
                
                m_basicEffect.ColorMap = m_texture;
                m_basicEffect.AmbientLightColor = Color.White;
                m_basicEffect.AmbientLightIntensity = 1f;
                CDare.Instance.GraphicsDevice.Indices = m_indexBuffer;
                CDare.Instance.GraphicsDevice.SetVertexBuffer(m_vertexBuffer);
                m_basicEffect.ProcessEffect(m_node.Render3D);
                foreach (EffectPass pass in m_basicEffect.Effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    
                    CDare.Instance.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);
                }
            }
            else
            {
                //Vector3 pos = CDare.Instance.GraphicsDevice.Viewport.Project(m_node.Position + m_offset, m_node.Render3D.CameraMgr.ActiveCamera.Projection, m_node.Render3D.CameraMgr.ActiveCamera.View, Matrix.CreateTranslation(Vector3.Zero));
                //m_basicEffect.World = Matrix.CreateTranslation(pos);//Matrix.CreateBillboard(pos + m_offset, m_node.Render3D.CameraMgr.ActiveCamera.Position, m_node.Render3D.CameraMgr.ActiveCamera.Up, null);
                /*m_basicEffect.World = Matrix.CreateBillboard(m_node.Position + (((m_node.Render3D.CameraMgr.ActiveCamera as DARE.CIsoCamera).Position - m_node.Position) * 0.5f) , m_node.Render3D.CameraMgr.ActiveCamera.Position, m_node.Render3D.CameraMgr.ActiveCamera.Up, m_node.Render3D.CameraMgr.ActiveCamera.Target);
                m_basicEffect.View = m_node.Render3D.CameraMgr.ActiveCamera.View;
                m_basicEffect.Projection = m_node.Render3D.CameraMgr.ActiveCamera.Projection;

                m_basicEffect.ColorMap = m_texture;
                m_basicEffect.AmbientColor = Color.White;
                m_basicEffect.AmbientIntensity = 1f;

                foreach (EffectPass pass in m_basicEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    CDare.Instance.GraphicsDevice.Indices = m_indexBuffer;
                    CDare.Instance.GraphicsDevice.SetVertexBuffer(m_vertexBuffer);
                    CDare.Instance.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);
                }*/
                
                Vector3 v = CDare.Instance.GraphicsDevice.Viewport.Project(Position, 
                    m_node.Render3D.CameraMgr.ActiveCamera.Projection, 
                    m_node.Render3D.CameraMgr.ActiveCamera.View, 
                    Matrix.CreateTranslation(Vector3.Zero));
                m_rect.X = (int)(v.X - (m_size.X / 2));
                m_rect.Y = (int)(v.Y - (m_size.Y / 2));
                m_rect.X += (int)SpriteOffset.X;
                m_rect.Y += (int)SpriteOffset.Y;
                m_rect.Width = (int)m_size.X;
                m_rect.Height = (int)m_size.Y;
                CDare.Instance.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.AnisotropicClamp, DepthStencilState.Default, RasterizerState.CullNone);//, m_basicEffect);//, Matrix.CreateRotationY(MathHelper.ToRadians(45)));
                if (m_texture != null)
                    CDare.Instance.SpriteBatch.Draw(m_texture, m_rect, m_color);
                CDare.Instance.SpriteBatch.End();
            }
        }

    #endregion

    #region attach/detach

        public CSprite3D AttachToEntity(CEntity entity)
        {
            entity.AttachSprite3D(this);
            return this;
        }

        public CSprite3D DetachFromEntity()
        {
            m_entity.DetachSprite3D(this);
            return this;
        }

    #endregion

    }
}
